library(Seurat)
library(conos)
Loading required package: igraph
Attaching package: ‘igraph’
The following object is masked from ‘package:scater’:
normalize
The following objects are masked from ‘package:DelayedArray’:
path, simplify
The following objects are masked from ‘package:dplyr’:
as_data_frame, groups, union
The following objects are masked from ‘package:purrr’:
compose, simplify
The following object is masked from ‘package:tidyr’:
crossing
The following object is masked from ‘package:tibble’:
as_data_frame
The following object is masked from ‘package:GenomicRanges’:
union
The following object is masked from ‘package:IRanges’:
union
The following object is masked from ‘package:S4Vectors’:
union
The following objects are masked from ‘package:BiocGenerics’:
normalize, path, union
The following objects are masked from ‘package:stats’:
decompose, spectrum
The following object is masked from ‘package:base’:
union
library(ggpubr)
library(tidyverse)
library(SingleCellExperiment)
# library(monocle3)
source("~/multiOmic_benchmark/utils.R")
source("~/multiOmic_benchmark/integrateBenchmark.R")
Based on the results of my benchmark, I set out to align expression and accessibility profiles from the F74 developing thymus dataset to detect changes in accessibility along pseudotime trajectories. While the benchmark was based on the task of label propagation, I here use the two most faithful methods (Seurat CCA and Conos) to achieve a common embedding of ATAC-seq and RNA-seq cells.
Load datasets.
rna.sce <- readRDS("~/my_data/F74_RNA_seurat_processed.RDS")
atac.sce <- readRDS("~/my_data/F74_ATAC_snapAtac_processed_bgmat.RDS")
Filter genes with zero variance
rna.gene.var <- as.matrix(counts(rna.sce)) %>% rowVars()
atac.gene.var <- as.matrix(counts(atac.sce)) %>% rowVars()
rna.sce <- rna.sce[which(rna.gene.var > 0),]
atac.sce <- atac.sce[which(atac.gene.var > 0),]
rna.sce; atac.sce
class: SingleCellExperiment
dim: 24510 8321
metadata(0):
assays(3): counts cpm logcounts
rownames(24510): RP11-34P13.3 RP11-34P13.7 ... AC233755.1
AC240274.1
rowData names(0):
colnames(8321): AAACCTGAGTTCGATC_1 AAACCTGCAAGTTGTC_1 ...
TTTGTCAAGCTGAACG_2 TTTGTCAGTATTAGCC_2
colData names(1): annotation
reducedDimNames(0):
spikeNames(0):
class: SingleCellExperiment
dim: 31122 5793
metadata(0):
assays(3): counts cpm logcounts
rownames(31122): A1BG A1BG-AS1 ... ZYX ZZEF1
rowData names(0):
colnames(5793): AAACGAAAGTGAACCG-1 AAACGAACATCGGCCA-1 ...
TTTGTGTTCGATCGCG-1 TTTGTGTTCTGAGTAC-1
colData names(28): orig.ident nCount_ATAC ... nFeature_ACTIVITY
ident
reducedDimNames(2): LSI UMAP
spikeNames(0):
Integration of T cells clusters
I re-run the integration based on the T cell subset. To select cells from the scATAC dataset, I take the SnapATAC clusters that best correspond to T-cells, based on label transfer.
tcells.sce.atac <- atac.sce[,which(as.numeric(atac.sce$seurat_clusters) %in% c(1:9))]
tcells.rna.ix <- which(rna.sce$annotation %in% c("DN","DP (Q)", "DP (P)", "SP (1)", "SP (2)"))
tcells.sce.rna <- rna.sce[,tcells.rna.ix]
tcells.sce.list <- list(RNA=tcells.sce.rna, ATAC=tcells.sce.atac)
Next, I select genes on which to perform integration. I take the union of the most variable features in the RNA dataset and the most covered features in the ATAC dataset


Remove cell cycle genes, that might interfere with pseudotime ordering
# integrate_features_ref <- hvg.rna
# integrate_features_ref <- setdiff(integrate_features_ref, cell_cycle_genes)
liger.obj <- scaleNotCenter(liger.obj, remove.missing = F)
Error in intI(i, n = x@Dim[1], dn[[1]], give.dn = FALSE) :
invalid character indexing
Visualize T cells in RNA dataset

Visualize markers

Visualize T cells in ATAC dataset: is the trajectory visible in the binary matrix?

Run CCA and Conos
Run CCA
coembed <- merge(x = seurat.list$RNA, y = seurat.list$ATAC)
coembed <- ScaleData(coembed, features = integrate_features_union, do.scale = FALSE)
Centering data matrix
|
| | 0%
|
|============== | 14%
|
|============================= | 29%
|
|=========================================== | 43%
|
|========================================================= | 57%
|
|======================================================================= | 71%
|
|====================================================================================== | 86%
|
|====================================================================================================| 100%
coembed <- RunPCA(coembed, features = integrate_features_union, verbose = FALSE)
coembed <- RunUMAP(coembed, dims = 1:30)
10:53:47 UMAP embedding parameters a = 0.9922 b = 1.112
10:53:47 Read 12078 rows and found 30 numeric columns
10:53:47 Using Annoy for neighbor search, n_neighbors = 30
10:53:47 Building Annoy index with metric = cosine, n_trees = 50
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:53:50 Writing NN index file to temp file /tmp/RtmplOAmA4/file511c460e48ef
10:53:50 Searching Annoy index using 1 thread, search_k = 3000
10:53:55 Annoy recall = 100%
10:53:59 Commencing smooth kNN distance calibration using 1 thread
10:54:03 Initializing from normalized Laplacian + noise
10:54:03 Commencing optimization for 200 epochs, with 562314 positive edges
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:54:17 Optimization finished
coembed <- AddMetaData(coembed, metadata = ifelse(colnames(coembed) %in% colnames(seurat.list[[reference]]), reference, query), col.name = "tech")
DimPlot(coembed, group.by = c('tech', "annotation"))

Run Conos


FeaturePlot(coembed, features = t.cell.markers$known.markers, split.by = "tech", slot = "data", cols = viridis::viridis(n=100))


Transfer labels on ATAC dataset

FeaturePlot(coembed, features = "prediction.score.max", cells = which(coembed$tech=="ATAC")) + scale_color_viridis_c()
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

Run Pseudotime analysis
Identify cell of origin based on IGLL1 and CD34

AddMetaData(coembed, ifelse(colnames(coembed)==cell.oo, TRUE, FALSE), col.name = "iroot_cell")
An object of class Seurat
55632 features across 12078 samples within 2 assays
Active assay: RNA (24510 features)
1 other assay present: ATAC
2 dimensional reductions calculated: pca, umap
merged.sce <- SingleCellExperiment(list(counts=coembed@assays$RNA@counts, logcounts=coembed@assays$RNA@data), colData=coembed@meta.data[, c("annotation", "tech", "iroot_cell")],
reducedDims = map(coembed@reductions, ~ .x@cell.embeddings))
saveRDS(object = merged.sce, "~/my_data/Tcells_CCA_integration_20191127.RDS")
saveRDS(object = integrate_features_union, "~/my_data/intFeatures_Tcells_CCA_integration_20191127.RDS")
dpt <- read.csv('~/my_data/Tcells_CCA_integration_20191127_scanpy_dpt.csv') %>%
select(X, dpt_pseudotime)
coembed <- AddMetaData(coembed, column_to_rownames(dpt, 'X'))


Check expression of markers along pseudotime
Bin ATAC cells by pseudotime

Fraction of accessible bins at each pseudotime bin


Motif analysis
Call peaks
peaks.ls = mclapply(seq(clusters.sel), function(i){
print(paste("cluster", clusters.sel[i]))
peaks = runMACS(
obj=snap.out[which(snap.out@metaData$barcode %in% colnames(tcells.sce.atac)[tcells.sce.atac$seurat_clusters==clusters.sel[i]]),],
output.prefix=paste0("Tcells_F74_", gsub("cluster", clusters.sel)[i]),
path.to.snaptools="/opt/conda/bin/snaptools",
path.to.macs="/opt/conda/bin/macs2",
gsize="hs", # mm, hs, etc
buffer.size=500,
num.cores=3,
macs.options="--nomodel --shift 100 --ext 200 --qval 5e-2 -B --SPMR",
tmp.folder=tempdir()
)
peaks
}, mc.cores=5)
all scheduled cores encountered errors in user code
Using chromVAR implementation in snapATAC
# snap.out = makeBinary(x.sp, "pmat")
snap.out@mmat = runChromVAR(
obj=snap.out[which(snap.out@metaData$barcode %in% dpt.bin.cells),],
input.mat="bmat",
genome=BSgenome.Hsapiens.UCSC.hg38,
# min.count=10,
species="Homo sapiens"
);
Epoch: checking depedent packages ...
Epoch: checking input parameters ...
Epoch: creating chromVAR object ...
Epoch: computing GC bias ...
Epoch: getting JASPAR motifs ...
There are 1 result in use. The connection will be released when they are closed

LS0tCnRpdGxlOiAiUHNldWRvdGltZSBhbmFseXNpcyBvZiBULWNlbGxzIGluIGRldmVsb3BpbmcgdGh5bXVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoY29ub3MpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKIyBsaWJyYXJ5KG1vbm9jbGUzKQpzb3VyY2UoIn4vbXVsdGlPbWljX2JlbmNobWFyay91dGlscy5SIikKc291cmNlKCJ+L211bHRpT21pY19iZW5jaG1hcmsvaW50ZWdyYXRlQmVuY2htYXJrLlIiKQpzb3VyY2UoIn4vbXVsdGlPbWljX2JlbmNobWFyay9wcmVwcm9jZXNzL3NlbGVjdEZlYXR1cmVzLlIiKQpgYGAKCkJhc2VkIG9uIHRoZSByZXN1bHRzIG9mIG15IGJlbmNobWFyaywgSSBzZXQgb3V0IHRvIGFsaWduIGV4cHJlc3Npb24gYW5kIGFjY2Vzc2liaWxpdHkgcHJvZmlsZXMgZnJvbSB0aGUgRjc0IGRldmVsb3BpbmcgdGh5bXVzIGRhdGFzZXQgdG8gZGV0ZWN0IGNoYW5nZXMgaW4gYWNjZXNzaWJpbGl0eSBhbG9uZyBwc2V1ZG90aW1lIHRyYWplY3Rvcmllcy4gV2hpbGUgdGhlIGJlbmNobWFyayB3YXMgYmFzZWQgb24gdGhlIHRhc2sgb2YgbGFiZWwgcHJvcGFnYXRpb24sIEkgaGVyZSB1c2UgdGhlIHR3byBtb3N0IGZhaXRoZnVsIG1ldGhvZHMgKFNldXJhdCBDQ0EgYW5kIENvbm9zKSB0byBhY2hpZXZlIGEgY29tbW9uIGVtYmVkZGluZyBvZiBBVEFDLXNlcSBhbmQgUk5BLXNlcSBjZWxscy4KCkxvYWQgZGF0YXNldHMuCgpgYGB7cn0Kcm5hLnNjZSA8LSByZWFkUkRTKCJ+L215X2RhdGEvRjc0X1JOQV9zZXVyYXRfcHJvY2Vzc2VkLlJEUyIpCmF0YWMuc2NlIDwtIHJlYWRSRFMoIn4vbXlfZGF0YS9GNzRfQVRBQ19zbmFwQXRhY19wcm9jZXNzZWRfYmdtYXQuUkRTIikKYGBgCgpGaWx0ZXIgZ2VuZXMgd2l0aCB6ZXJvIHZhcmlhbmNlCmBgYHtyfQpybmEuZ2VuZS52YXIgPC0gYXMubWF0cml4KGNvdW50cyhybmEuc2NlKSkgJT4lIHJvd1ZhcnMoKQphdGFjLmdlbmUudmFyIDwtIGFzLm1hdHJpeChjb3VudHMoYXRhYy5zY2UpKSAlPiUgcm93VmFycygpCgpybmEuc2NlIDwtIHJuYS5zY2Vbd2hpY2gocm5hLmdlbmUudmFyID4gMCksXQphdGFjLnNjZSA8LSBhdGFjLnNjZVt3aGljaChhdGFjLmdlbmUudmFyID4gMCksXQoKcm5hLnNjZTsgYXRhYy5zY2UKYGBgCgoKIyMgSW50ZWdyYXRpb24gb2YgVCBjZWxscyBjbHVzdGVycwpJIHJlLXJ1biB0aGUgaW50ZWdyYXRpb24gYmFzZWQgb24gdGhlIFQgY2VsbCBzdWJzZXQuIFRvIHNlbGVjdCBjZWxscyBmcm9tIHRoZSBzY0FUQUMgZGF0YXNldCwgSSB0YWtlIHRoZSBTbmFwQVRBQyBjbHVzdGVycyB0aGF0IGJlc3QgY29ycmVzcG9uZCB0byBULWNlbGxzLCBiYXNlZCBvbiBsYWJlbCB0cmFuc2Zlci4KCmBgYHtyfQp0Y2VsbHMuc2NlLmF0YWMgPC0gYXRhYy5zY2VbLHdoaWNoKGFzLm51bWVyaWMoYXRhYy5zY2Ukc2V1cmF0X2NsdXN0ZXJzKSAlaW4lIGMoMTo5KSldCgp0Y2VsbHMucm5hLml4IDwtIHdoaWNoKHJuYS5zY2UkYW5ub3RhdGlvbiAlaW4lIGMoIkROIiwiRFAgKFEpIiwgIkRQIChQKSIsICJTUCAoMSkiLCAiU1AgKDIpIikpCnRjZWxscy5zY2Uucm5hIDwtIHJuYS5zY2VbLHRjZWxscy5ybmEuaXhdCgp0Y2VsbHMuc2NlLmxpc3QgPC0gbGlzdChSTkE9dGNlbGxzLnNjZS5ybmEsIEFUQUM9dGNlbGxzLnNjZS5hdGFjKQpgYGAKCk5leHQsIEkgc2VsZWN0IGdlbmVzIG9uIHdoaWNoIHRvIHBlcmZvcm0gaW50ZWdyYXRpb24uIEkgdGFrZSB0aGUgdW5pb24gb2YgdGhlIG1vc3QgdmFyaWFibGUgZmVhdHVyZXMgaW4gdGhlIFJOQSBkYXRhc2V0IGFuZCB0aGUgbW9zdCBjb3ZlcmVkIGZlYXR1cmVzIGluIHRoZSBBVEFDIGRhdGFzZXQKCmBgYHtyfQpoY2cuYXRhYyA8LSBzZWxlY3RfaGlnaGx5Q292ZXJlZCh0Y2VsbHMuc2NlLmxpc3QkQVRBQywgZnJhY19jZWxscyA9IDAuMikKaHZnLnJuYSA8LSBzZWxlY3RfaGlnaGx5VmFyaWFibGUodGNlbGxzLnNjZS5saXN0JFJOQSkKClVwU2V0Ujo6dXBzZXQoVXBTZXRSOjpmcm9tTGlzdChsaXN0KEhWRy5STkE9aHZnLnJuYSwgSENHLkFUQUM9aGNnLmF0YWMpKSkKYGBgCgpSZW1vdmUgY2VsbCBjeWNsZSBnZW5lcywgdGhhdCBtaWdodCBpbnRlcmZlcmUgd2l0aCBwc2V1ZG90aW1lIG9yZGVyaW5nCmBgYHtyfQpjZWxsX2N5Y2xlX2dlbmVzIDwtIHJlYWQudGFibGUoIn4vYW5ub3RhdGlvbnMvY2VsbF9jeWNsZV9nZW5lcy50c3YiKSRWMQoKaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIDwtIHVuaW9uKGh2Zy5ybmEsIGhjZy5hdGFjKQppbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24gPC0gc2V0ZGlmZihpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIGNlbGxfY3ljbGVfZ2VuZXMpIAoKIyMgU2VsZWN0IGZlYXR1cmVzIGluIGJvdGggZGF0YXNldHMKaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIDwtIGludGVyc2VjdChpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIGludGVyc2VjdChyb3duYW1lcyh0Y2VsbHMuc2NlLmxpc3QkQVRBQyksIHJvd25hbWVzKHRjZWxscy5zY2UubGlzdCRSTkEpKSkgCgpsZW5ndGgoaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uKQojIGludGVncmF0ZV9mZWF0dXJlc19yZWYgPC0gaHZnLnJuYSAKIyBpbnRlZ3JhdGVfZmVhdHVyZXNfcmVmIDwtIHNldGRpZmYoaW50ZWdyYXRlX2ZlYXR1cmVzX3JlZiwgY2VsbF9jeWNsZV9nZW5lcykgCmxpZ2VyLm9iaiA8LSBzY2FsZU5vdENlbnRlcihsaWdlci5vYmosIHJlbW92ZS5taXNzaW5nID0gRikKYGBgCgpWaXN1YWxpemUgVCBjZWxscyBpbiBSTkEgZGF0YXNldApgYGB7cn0KdGNlbGxzLlJOQS51bmlvbiA8LSB0Y2VsbHMuc2V1Lmxpc3QkUk5BClZhcmlhYmxlRmVhdHVyZXModGNlbGxzLlJOQS51bmlvbikgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uCnRjZWxscy5STkEudW5pb24gPC0gU2NhbGVEYXRhKHRjZWxscy5STkEudW5pb24pICU+JSBSdW5QQ0EoKSAlPiUgUnVuVU1BUChkaW1zPTE6NDApCgpEaW1QbG90KHRjZWxscy5STkEudW5pb24sIGdyb3VwLmJ5ID0gImFubm90YXRpb24iLCBsYWJlbD1UUlVFKSArIGdndGl0bGUoIlJOQSAtIGZlYXR1cmUgdW5pb24iKQpgYGAKClZpc3VhbGl6ZSBtYXJrZXJzIApgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTEwfQp0LmNlbGwubWFya2VycyA8LSBsaXN0KGtub3duLm1hcmtlcnMgPSBjKCJDRDM0IiwgIklHTEwxIiwgIlRSR0MyIiwgIlRSREMiLCAiUFRDUkEiLCAiVFJCQzIiLCAiVFJBQyIsICJDRDQiLCAiQ0Q4QSIsICJDRDhCIiksCiAgICAgICAgICAgICAgICAgICAgICAgY2hlbW9raW5lLnJlY2VwdG9ycyA9IGMoIkNDUjkiLCAiQ0NSNyIpLAogICAgICAgICAgICAgICAgICAgICAgIHRjci5hY3RpdmF0aW9uID0gYygiQ0Q1IiwgIkNEMjciKSwKICAgICAgICAgICAgICAgICAgICAgICBwcm9saWZlcmF0aW9uPWMoIlBDTkEiLCAiQ0RLMSIsICJNS0k2NyIpLAogICAgICAgICAgICAgICAgICAgICAgIGN5Y2xpbi5EID0gYygiQ0NORDIiLCAiQ0NORDMiKSwKICAgICAgICAgICAgICAgICAgICAgICByZWNvbWJpbmF0aW9uPWMoIlJBRzEiLCAiUkFHMiIpLAogICAgICAgICAgICAgICAgICAgICAgIGFwb3B0b3Npcz1jKCJIUksiLCJCTUYiLCAiVFA1M0lOUDEiKSwKICAgICAgICAgICAgICAgICAgICAgICBzdGFnZS5tYXJrZXJzID0gYygiU1QxOCIsICJISVZFUDMiLCAiUkdQRDMiLCAiU01QRDMiLCAiQVFQMyIsICJST1JDIiwgIlNBVEIxIiwgIlRPWDIiKQogICAgICAgICAgICAgICAgICAgICAgICkgCiMgRmVhdHVyZVBsb3QodGNlbGxzLlJOQS5yZWYsIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMCkpCkZlYXR1cmVQbG90KHRjZWxscy5STkEudW5pb24sIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMCkpCmBgYAoKVmlzdWFsaXplIFQgY2VsbHMgaW4gQVRBQyBkYXRhc2V0OiBpcyB0aGUgdHJhamVjdG9yeSB2aXNpYmxlIGluIHRoZSBiaW5hcnkgbWF0cml4PwpgYGB7cn0KdGNlbGxzLkFUQUMudW5pb24gPC0gdGNlbGxzLnNldS5saXN0JEFUQUMKIyB0Y2VsbHMuQVRBQy51bmlvbiA8LSBOb3JtYWxpemVEYXRhKHRjZWxscy5BVEFDLnVuaW9uKQpWYXJpYWJsZUZlYXR1cmVzKHRjZWxscy5BVEFDLnVuaW9uKSA8LSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24KdGNlbGxzLkFUQUMudW5pb24gPC0gUnVuTFNJKHRjZWxscy5BVEFDLnVuaW9uLCBuPTUwLCBzY2FsZS5tYXggPSBOVUxMKQp0Y2VsbHMuQVRBQy51bmlvbiA8LSBSdW5VTUFQKHRjZWxscy5BVEFDLnVuaW9uLCByZWR1Y3Rpb24gPSAibHNpIiwgZGltcyA9IDE6NTApCgpEaW1QbG90KHRjZWxscy5BVEFDLnVuaW9uLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsIGxhYmVsID0gVFJVRSkgKyBnZ3RpdGxlKCJBVEFDIGdtYXQiKQpgYGAKCiMjIyMgUnVuIENDQSBhbmQgQ29ub3MKClJ1biBDQ0EKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD01fQpzY2UubGlzdCA8LSB0Y2VsbHMuc2NlLmxpc3QKcmVmZXJlbmNlID0gIlJOQSIKcXVlcnkgPSAiQVRBQyIgCnNldXJhdC5saXN0IDwtIGltYXAoc2NlLmxpc3QsIH4gYXMuU2V1cmF0KC54LCBhc3NheT0ueSkpCnNldXJhdC5saXN0IDwtIGltYXAoc2V1cmF0Lmxpc3QsIH4gUmVuYW1lQ2VsbHMoLngsIGFkZC5jZWxsLmlkPS55KSkKIyMgU2NhbGUgZGF0YQpzZXVyYXQubGlzdCA8LSBtYXAoc2V1cmF0Lmxpc3QsIH4gU2NhbGVEYXRhKC54KSkKIyMgQ2FsY3VsYXRlIENDQSBhbmNob3JzCnRyYW5zZmVyLmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXVyYXQubGlzdFtbcmVmZXJlbmNlXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnkgPSBzZXVyYXQubGlzdFtbcXVlcnldXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJjY2EiKQoKIyMgSW1wdXRlIGV4cHJlc3Npb24gcHJvZmlsZXMgZm9yIEFUQUMgY2VsbHMgKGZvciBhbGwgZ2VuZXMsIG5vdCBqdXN0IGludGVncmF0aW9uIGZlYXR1cmVzKQpyZWZkYXRhIDwtIEdldEFzc2F5RGF0YShzZXVyYXQubGlzdCRSTkEsIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiZGF0YSIpCmltcHV0YXRpb24gPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IHRyYW5zZmVyLmFuY2hvcnMsIHJlZmRhdGEgPSByZWZkYXRhLCB3ZWlnaHQucmVkdWN0aW9uID0gc2V1cmF0Lmxpc3QkQVRBQ1tbIkxTSSJdXSkKCiMjIE1lcmdlIGRhdGFzZXRzIGFuZCBjby1lbWJlZApzZXVyYXQubGlzdCRBVEFDW1siUk5BIl1dIDwtIGltcHV0YXRpb24KY29lbWJlZCA8LSBtZXJnZSh4ID0gc2V1cmF0Lmxpc3QkUk5BLCB5ID0gc2V1cmF0Lmxpc3QkQVRBQykKCmNvZW1iZWQgPC0gU2NhbGVEYXRhKGNvZW1iZWQsIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLCBkby5zY2FsZSA9IEZBTFNFKQpjb2VtYmVkIDwtIFJ1blBDQShjb2VtYmVkLCBmZWF0dXJlcyA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgdmVyYm9zZSA9IEZBTFNFKQpjb2VtYmVkIDwtIFJ1blVNQVAoY29lbWJlZCwgZGltcyA9IDE6MzApCgpjb2VtYmVkIDwtIEFkZE1ldGFEYXRhKGNvZW1iZWQsIG1ldGFkYXRhID0gaWZlbHNlKGNvbG5hbWVzKGNvZW1iZWQpICVpbiUgY29sbmFtZXMoc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dKSwgcmVmZXJlbmNlLCBxdWVyeSksIGNvbC5uYW1lID0gInRlY2giKQoKRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9IGMoJ3RlY2gnLCAiYW5ub3RhdGlvbiIpKQpgYGAKClJ1biBDb25vcwpgYGB7cn0KZGF0YS5wcm9jZXNzZWQgPC0gbWFwKHNjZS5saXN0LCB+IGFzLlNldXJhdCgueCkpIApWYXJpYWJsZUZlYXR1cmVzKGRhdGEucHJvY2Vzc2VkW1tyZWZlcmVuY2VdXSkgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uClZhcmlhYmxlRmVhdHVyZXMoZGF0YS5wcm9jZXNzZWRbW3F1ZXJ5XV0pIDwtIGludGVncmF0ZV9mZWF0dXJlc191bmlvbgpkYXRhLnByb2Nlc3NlZCA8LSBtYXAoZGF0YS5wcm9jZXNzZWQsIH4gU2NhbGVEYXRhKC54KSAlPiUgUnVuUENBKGRpbXM9MTozMCkpCmwuY29uIDwtIENvbm9zJG5ldyhkYXRhLnByb2Nlc3NlZCxuLmNvcmVzPTMwKQpsLmNvbiRidWlsZEdyYXBoKGs9MTUsay5zZWxmPTUsay5zZWxmLndlaWdoPTAuMDEsbmNvbXBzPTMwLG4ub2RnZW5lcz01ZTMsc3BhY2U9J1BDQScpIAoKbC5jb24kZmluZENvbW11bml0aWVzKHJlc29sdXRpb249MS41KQpsLmNvbiRlbWJlZEdyYXBoKGFscGhhPTEvMikKICAKY29ub3Mub3V0IDwtIGNvbm9zLm1vZGVsJG1vZGVsCmwuY29uJHBsb3RHcmFwaChjb2xvci5ieSA9ICJzYW1wbGUiKQoKZ2VuZVggPC0gc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dQGFzc2F5cyRSTkFAc2NhbGUuZGF0YVszLF0KZ2VuZVggPC0gc2V0TmFtZXMoYW5ub3RhdGlvblssMV0sIHJvd25hbWVzKGFubm90YXRpb24pKQpuZXcubGFiZWwucHJvYmFiaWxpdGllcyA8LSBsLmNvbiRwcm9wYWdhdGVMYWJlbHMobGFiZWxzID0gZ2VuZVgsIHZlcmJvc2UgPSBULCBmaXhlZC5pbml0aWFsLmxhYmVscz1UKQpoaXN0KG5ldy5sYWJlbC5wcm9iYWJpbGl0aWVzKQpsLmNvbiRjb3JyZWN0R2VuZXMoZ2VuZXMgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIGNvdW50Lm1hdHJpeCA9IE1hdHJpeChzZXVyYXQubGlzdCRBVEFDQGFzc2F5cyRBVEFDQGRhdGEpKQoKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0yMCwgZmlnLndpZHRoPTl9CkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMkc3RhZ2UubWFya2Vycywgc3BsaXQuYnkgPSAidGVjaCIsIGNvbHMgPSB2aXJpZGlzOjp2aXJpZGlzKG49MTAwKSkgKyBnZ3RpdGxlKCJTdGFnZSBNYXJrZXJzIikKYGBgCmBgYHtyLCBmaWcuaGVpZ2h0PTI1LCBmaWcud2lkdGg9OX0KRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSB0LmNlbGwubWFya2VycyRrbm93bi5tYXJrZXJzLCBzcGxpdC5ieSA9ICJ0ZWNoIiwgc2xvdCA9ICJkYXRhIiwgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMDApKQpgYGAKYGBge3J9CkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMkcmVjb21iaW5hdGlvbiwgc3BsaXQuYnkgPSAidGVjaCIsIHNsb3QgPSAiZGF0YSIsIGNvbHMgPSB2aXJpZGlzOjp2aXJpZGlzKG49MTAwKSkgCmBgYAoKVHJhbnNmZXIgbGFiZWxzIG9uIEFUQUMgZGF0YXNldApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTV9CmNlbGx0eXBlLnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSB0cmFuc2Zlci5hbmNob3JzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZmRhdGEgPSBzZXVyYXQubGlzdFtbcmVmZXJlbmNlXV0kYW5ub3RhdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQucmVkdWN0aW9uID0gc2V1cmF0Lmxpc3QkQVRBQ1tbIkxTSSJdXSkKY2VsbC50eXBlcyA8LSB1bmlxdWUoY29lbWJlZCRhbm5vdGF0aW9uKQpjZWxsLnR5cGUucGFsIDwtIGJyZXdlcl9wYWxldHRlXzRfdmFsdWVzKGNlbGwudHlwZXMsIHBhbGV0dGUgPSAiU2V0MSIpICU+JSBzZXROYW1lcyhjZWxsLnR5cGVzKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBtZXRhZGF0YSA9IGNlbGx0eXBlLnByZWRpY3Rpb25zKQpjb2VtYmVkQG1ldGEuZGF0YSAlPD4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lCiAgZHBseXI6Om11dGF0ZShhbm5vdGF0aW9uPWlmZWxzZShpcy5uYShwcmVkaWN0ZWQuaWQpICwgYW5ub3RhdGlvbiwgTkEpKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoKQoKY29lbWJlZEBtZXRhLmRhdGEgPC0KICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBkcGx5cjo6bXV0YXRlKGFubm90YXRpb249aWZlbHNlKGlzLm5hKGFubm90YXRpb24pICYgcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjUsIHByZWRpY3RlZC5pZCwgYW5ub3RhdGlvbikpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygpCkNvbWJpbmVQbG90cygKICBsaXN0KERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJwcmVkaWN0ZWQuaWQiKSwgY29scyA9IGNlbGwudHlwZS5wYWwpICsgZ2d0aXRsZSgicHJlZGljdGlvbiIpLAogIERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJhbm5vdGF0aW9uIiksIGNvbHMgPSBjZWxsLnR5cGUucGFsKSArIGdndGl0bGUoIk9yaWdpbmFsICsgcHJlZGljdGlvbiIpKSwKICBsZWdlbmQgPSAidG9wIgogICkKYGBgCmBgYHtyfQpGZWF0dXJlUGxvdChjb2VtYmVkLCBmZWF0dXJlcyA9ICJwcmVkaWN0aW9uLnNjb3JlLm1heCIsIGNlbGxzID0gd2hpY2goY29lbWJlZCR0ZWNoPT0iQVRBQyIpKSArIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpCmBgYAoKCiMjIyBSdW4gUHNldWRvdGltZSBhbmFseXNpcyAKSWRlbnRpZnkgY2VsbCBvZiBvcmlnaW4gYmFzZWQgb24gSUdMTDEgYW5kIENEMzQKYGBge3IsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0xNn0KRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSBjKCJJR0xMMSIsICJDRDM0IiksIHNwbGl0LmJ5ID0gInRlY2giLCBzbG90ID0gImRhdGEiLCBjb2xzID0gdmlyaWRpczo6dmlyaWRpcyhuPTEwMCkpCmBgYApgYGB7cn0KY2VsbC5vbyA8LQogIGNvZW1iZWRAbWV0YS5kYXRhICU+JSAKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICBtdXRhdGUoSUdMTDE9Y29lbWJlZEBhc3NheXMkUk5BQGNvdW50c1siSUdMTDEiLGNlbGxdKSAlPiUKICBzZWxlY3QoY2VsbCwgYW5ub3RhdGlvbiwgSUdMTDEpICU+JQogIGFycmFuZ2UoLUlHTEwxKSAlPiUKICBmaWx0ZXIoYW5ub3RhdGlvbj09IkROIikgJT4lCiAgdG9wX24oMSwgSUdMTDEpICU+JQogIHB1bGwoY2VsbCkKCmNvZW1iZWRAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy50aWJibGUocm93bmFtZXM9ImNlbGwiKSAlPiUKICBtdXRhdGUoY2VsbC5vbyA9IGlmZWxzZShjZWxsICVpbiUgY2VsbC5vbywgVCwgRikpICU+JQogIGdncGxvdChhZXMoVU1BUF8xLCBVTUFQXzIpKSArCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleTUwIikgKwogIGdlb21fcG9pbnQoZGF0YT0uICU+JSBmaWx0ZXIoY2VsbC5vbyksY29sb3I9J3JlZCcpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YT0uICU+JSBmaWx0ZXIoY2VsbC5vbyksIGFlcyhsYWJlbD0iY2VsbCBvZiBvcmlnaW4iKSwgY29sb3I9J3JlZCcpICsKICB0aGVtZV9jb3dwbG90KCkgCgpjb2VtYmVkIDwtIEFkZE1ldGFEYXRhKGNvZW1iZWQsIGlmZWxzZShjb2xuYW1lcyhjb2VtYmVkKT09Y2VsbC5vbywgVFJVRSwgRkFMU0UpLCBjb2wubmFtZSA9ICJpcm9vdF9jZWxsIikKCiAgCmBgYAoKCmBgYHtyfQptZXJnZWQuc2NlIDwtIFNpbmdsZUNlbGxFeHBlcmltZW50KGxpc3QoY291bnRzPWNvZW1iZWRAYXNzYXlzJFJOQUBjb3VudHMsIGxvZ2NvdW50cz1jb2VtYmVkQGFzc2F5cyRSTkFAZGF0YSksIGNvbERhdGE9Y29lbWJlZEBtZXRhLmRhdGFbLCBjKCJhbm5vdGF0aW9uIiwgInRlY2giLCAiaXJvb3RfY2VsbCIpXSwKICAgICAgICAgICAgICAgICAgICAgcmVkdWNlZERpbXMgPSBtYXAoY29lbWJlZEByZWR1Y3Rpb25zLCB+IC54QGNlbGwuZW1iZWRkaW5ncykpCgpzYXZlUkRTKG9iamVjdCA9IG1lcmdlZC5zY2UsICJ+L215X2RhdGEvVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl8yMDE5MTEyNy5SRFMiKQpzYXZlUkRTKG9iamVjdCA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgIn4vbXlfZGF0YS9pbnRGZWF0dXJlc19UY2VsbHNfQ0NBX2ludGVncmF0aW9uXzIwMTkxMTI3LlJEUyIpCmBgYAoKCmBgYHtyfQpkcHQgPC0gcmVhZC5jc3YoJ34vbXlfZGF0YS9UY2VsbHNfQ0NBX2ludGVncmF0aW9uXzIwMTkxMTI3X3NjYW5weV9kcHQuY3N2JykgJT4lCiAgc2VsZWN0KFgsIGRwdF9wc2V1ZG90aW1lKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBjb2x1bW5fdG9fcm93bmFtZXMoZHB0LCAnWCcpKQpzYXZlUkRTKGNvZW1iZWQsICJ+L215X2RhdGEvVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl9zZXVyYXRfMjAxOTExMjcuUm1kIikKYGBgCgpgYGB7cn0KRmVhdHVyZVBsb3QoY29lbWJlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBmZWF0dXJlID0gImRwdF9wc2V1ZG90aW1lIiwgc3BsaXQuYnkgPSAidGVjaCIsIGNvbD12aXJpZGlzOjp2aXJpZGlzKDEwKSkgCmBgYApgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CmNvZW1iZWRAbWV0YS5kYXRhICU+JQogIGRwbHlyOjptdXRhdGUoYERQVCByYW5rYD1kZW5zZV9yYW5rKGRwdF9wc2V1ZG90aW1lKSkgJT4lCiAgZ2dwbG90KGFlcyhgRFBUIHJhbmtgKSkgKwogICMgZ2dwbG90KGFlcyhkcHRfcHNldWRvdGltZSkpICsKICAjIGdlb21fcG9pbnQoYWVzKGNvbG9yPWFubm90YXRpb24pKSArCiAgIyBmYWNldF9ncmlkKGFubm90YXRpb25+LikKICBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbD1hbm5vdGF0aW9uKSwgYmlucz01MCkgKwogIGZhY2V0X2dyaWQoYW5ub3RhdGlvbn50ZWNoKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjZWxsLnR5cGUucGFsKQoKYGBgCgpDaGVjayBleHByZXNzaW9uIG9mIG1hcmtlcnMgYWxvbmcgcHNldWRvdGltZQpgYGB7cn0KY29lbWJlZEBhc3NheXMkUk5BQGRhdGFbdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgXSAlPiUKICBhcy5tYXRyaXgoKSAlPiUKICByZXNoYXBlMjo6bWVsdCh2YXJuYW1lcz1jKCJnZW5lIiwgImNlbGwiKSkgJT4lCiAgZ3JvdXBfYnkoZ2VuZSkgJT4lCiAgIyBtdXRhdGUodmFsdWU9KHZhbHVlLW1lYW4odmFsdWUpKS9zZCh2YWx1ZSkpICU+JQogIGxlZnRfam9pbihjb2VtYmVkQG1ldGEuZGF0YVssImRwdF9wc2V1ZG90aW1lIiwgZHJvcD1GXSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikpICU+JQogIG11dGF0ZShwc2V1ZG90aW1lLnJhbms9ZGVuc2VfcmFuayhkcHRfcHNldWRvdGltZSkpICU+JQogIGdncGxvdChhZXMocHNldWRvdGltZS5yYW5rLCBnZW5lKSkgKwogIGdlb21fdGlsZShhZXMoZmlsbD12YWx1ZSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpCmBgYAoKQmluIEFUQUMgY2VsbHMgYnkgcHNldWRvdGltZQpgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTR9CmRwdC5kZiA8LSAKICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICAjIGZpbHRlcih0ZWNoPT0iQVRBQyIpICU+JQogICMgZ3JvdXBfYnkodGVjaCkgJT4lCiAgZHBseXI6Om11dGF0ZShkcHRfcmFuaz1kZW5zZV9yYW5rKGRwdF9wc2V1ZG90aW1lKSkgJT4lCiAgbXV0YXRlKGRwdF9iaW49Y3V0KGRwdF9yYW5rLCBicmVha3MgPSAxMDApKSAlPiUKICBtdXRhdGUoZHB0X2Jpbj1hcy5udW1lcmljKGRwdF9iaW4pKSAlPiUKICAjIHVuZ3JvdXAoKSAlPiUKICBzZWxlY3QoY2VsbCx0ZWNoLCBhbm5vdGF0aW9uLCBwcmVkaWN0aW9uLnNjb3JlLm1heCwgZHB0X2JpbiwgZHB0X3BzZXVkb3RpbWUpCgpjZWxsLnR5cGUucGwgPC0gZHB0LmRmICU+JQogIGdncGxvdChhZXMoZHB0X2JpbiwgZmlsbCA9IGFubm90YXRpb24pKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSArCiAgZmFjZXRfZ3JpZCh0ZWNofi4pICsKICB4bGFiKCJQc2V1ZG90aW1lIGJpbiIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikKCmNlbGwudHlwZS5wbApgYGAKCkZyYWN0aW9uIG9mIGFjY2Vzc2libGUgYmlucyBhdCBlYWNoIHBzZXVkb3RpbWUgYmluIApgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTh9CnNuYXAub3V0IDwtIHJlYWRSRFMoZmlsZSA9ICJ+L215X2RhdGEvY2VsbHJhbmdlci1hdGFjMTEwX2NvdW50XzMwNDM5X1dTU1M4MDM4MzYwX0dSQ2gzOC0xXzFfMC5zbmFwQVRBQy5SRFMiKQphdGFjLmRwdC5kZiA8LSAKICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICBmaWx0ZXIodGVjaD09IkFUQUMiKSAlPiUKICAjIGdyb3VwX2J5KHRlY2gpICU+JQogIGRwbHlyOjptdXRhdGUoZHB0X3Jhbms9ZGVuc2VfcmFuayhkcHRfcHNldWRvdGltZSkpICU+JQogIG11dGF0ZShkcHRfYmluPWN1dChkcHRfcmFuaywgYnJlYWtzID0gMTAwKSkgJT4lCiAgbXV0YXRlKGRwdF9iaW49YXMubnVtZXJpYyhkcHRfYmluKSkgJT4lCiAgIyB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KGNlbGwsdGVjaCwgYW5ub3RhdGlvbiwgcHJlZGljdGlvbi5zY29yZS5tYXgsIGRwdF9iaW4sIGRwdF9wc2V1ZG90aW1lKQoKY2VsbC50eXBlLnBsIDwtIGF0YWMuZHB0LmRmICU+JQogIGdncGxvdChhZXMoZHB0X2JpbiwgZmlsbCA9IGFubm90YXRpb24pKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSArCiAgeGxhYigiUHNldWRvdGltZSBiaW4iKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpCgpncm91cHMgPC0gYXRhYy5kcHQuZGZbLCBjKCJjZWxsIiwgImRwdF9iaW4iKV0KYm1hdCA8LSBzbmFwLm91dEBibWF0W3N0cl9yZW1vdmUoZ3JvdXBzJGNlbGwsICJBVEFDXyIpLF0KZnJhYy5hY2Nlc3NpYmxlIDwtIHJvd1N1bXMoYm1hdCkvbmNvbChibWF0KQphY2MuZnJhY3Rpb24ucGwgPC0gZ3JvdXBzICU+JQogIG11dGF0ZShmcmFjX2FjY2Vzc2libGU9ZnJhYy5hY2Nlc3NpYmxlW3N0cl9yZW1vdmUoY2VsbCwgIkFUQUNfIildKSAlPiUKICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIGZyYWNfYWNjZXNzaWJsZSkpICsKICBnZW9tX2JveHBsb3QoYWVzKGdyb3VwPWFzLmZhY3RvcihkcHRfYmluKSksIG91dGxpZXIuYWxwaGEgPSAwLjMpICsKICAjIGdlb21faml0dGVyKGFscGhhPTAuMSkgKwogIHhsYWIoIlBzZXVkb3RpbWUgYmluIikgKwogIHlsYWIoIkZyYWN0aW9uIG9mIGFjY2Vzc2libGUgYmlucyIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgCiAgCmFjYy5mcmFjdGlvbi5wbCAKcGxvdF9ncmlkKGNlbGwudHlwZS5wbCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSwgCiAgICAgICAgICBhY2MuZnJhY3Rpb24ucGwsIAogICAgICAgICAgYWxpZ24gPSAidiIsIG5jb2w9MSwgbnJvdz0yLCBheGlzPSJsIikKYGBgCgoKCmBgYHtyfQphY2MubWF0IDwtIGNvZW1iZWRAYXNzYXlzJEFUQUNAZGF0YQptYXJrZXJzLmFjYyA8LSBhY2MubWF0W2ludGVyc2VjdChjKHQuY2VsbC5tYXJrZXJzJGtub3duLm1hcmtlcnMsIHQuY2VsbC5tYXJrZXJzJGNoZW1va2luZS5yZWNlcHRvcnMsIHQuY2VsbC5tYXJrZXJzJHJlY29tYmluYXRpb24pLCByb3duYW1lcyhhY2MubWF0KSksLCBkcm9wPUZdCgptYXJrZXJzLmRmIDwtIGRhdGEuZnJhbWUodChhcy5tYXRyaXgobWFya2Vycy5hY2NbLGRwdC5kZiRjZWxsW2RwdC5kZiR0ZWNoPT0iQVRBQyJdXSkpKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHJvd25hbWVzKG1hcmtlcnMuYWNjKSwgbmFtZXNfdG8gPSAibWFya2VyLmdlbmUiLCB2YWx1ZXNfdG8gPSAiYWNjZXNzaWJpbGl0eSIpCgphbm5vdGF0aW9uLmhtIDwtIGF0YWMuZHB0LmRmICU+JQogIGdyb3VwX2J5KGRwdF9iaW4sIGFubm90YXRpb24pICU+JQogIHN1bW1hcmlzZShuPW4oKSkgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBhbm5vdGF0aW9uKSkgKwogIGdlb21fdGlsZShhZXMoYWxwaGE9biwgZmlsbD1hbm5vdGF0aW9uKSkgICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSArCiAgZ3VpZGVzKGZpbGw9J25vbmUnLCBhbHBoYT0nbm9uZScpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpCgptYXJrZXJzLmhtIDwtIGF0YWMuZHB0LmRmICU+JQogIGZ1bGxfam9pbihtYXJrZXJzLmRmKSAlPiUKICBncm91cF9ieShkcHRfYmluLCBtYXJrZXIuZ2VuZSkgJT4lCiAgc3VtbWFyaXNlKGZyYWNfYWNjZXNzaWJsZT1zdW0oYWNjZXNzaWJpbGl0eSkvbigpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKG1hcmtlci5nZW5lPWZhY3RvcihtYXJrZXIuZ2VuZSwgbGV2ZWxzID0gYygiQ0QzNCIsICJJR0xMMSIsICJUUkdDMiIsICJUUkRDIiwgIlBUQ1JBIiwgIlRSQkMyIiwgIkNDUjkiLCJDQ1I3IiwgIlJBRzEiLCAiUkFHMiIsICJUUkFDIiwgIkNENCIsICJDRDhBIiwgIkNEOEIiKSkpICU+JQogIG11dGF0ZShtYXJrZXIuZ2VuZT1mYWN0b3IobWFya2VyLmdlbmUsIGxldmVscyA9IHJldihsZXZlbHMobWFya2VyLmdlbmUpKSkpICU+JQogIGdyb3VwX2J5KG1hcmtlci5nZW5lKSAlPiUKICBtdXRhdGUoZnJhY19hY2Nlc3NpYmxlPShmcmFjX2FjY2Vzc2libGUgLSBtaW4oZnJhY19hY2Nlc3NpYmxlKSkvbWF4KGZyYWNfYWNjZXNzaWJsZSkgLSBtaW4oZnJhY19hY2Nlc3NpYmxlKSkgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBtYXJrZXIuZ2VuZSwgZmlsbD1mcmFjX2FjY2Vzc2libGUpKSArIAogIGdlb21fdGlsZSgpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhuYW1lPSJGcmFjLmNlbGxzIikgKwogIHhsYWIoIlBzZXVkb3RpbWUgYmluIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQoKbGVnIDwtIGdldF9sZWdlbmQobWFya2Vycy5obSkKZ3IxIDwtIHBsb3RfZ3JpZChhbm5vdGF0aW9uLmhtLCBtYXJrZXJzLmhtICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgbnJvdz0yLCByZWxfaGVpZ2h0cyA9IGMoMSwyKSwgYWxpZ24gPSAidiIpCmdyMiA8LSBwbG90X2dyaWQoZ2dwbG90KCkgKyAgdGhlbWVfdm9pZCgpLGxlZywgbnJvdz0yLCByZWxfaGVpZ2h0cyA9IGMoMSwyKSkKcGxvdF9ncmlkKGdyMSwgZ3IyLCByZWxfd2lkdGhzID0gYygzLDEpKQpgYGAKCiMjIE1vdGlmIGFuYWx5c2lzIAoKQ2FsbCBwZWFrcyAKYGBge3J9CiMjIENhbGwgcGVha3Mgb24gY2x1c3RlcnMKY2x1c3RlcnMuc2VsIDwtIHVuaXF1ZSh0Y2VsbHMuc2NlLmF0YWMkc2V1cmF0X2NsdXN0ZXJzKQpwZWFrcy5scyA9IG1jbGFwcGx5KHNlcShjbHVzdGVycy5zZWwpLCBmdW5jdGlvbihpKXsKICBwcmludChwYXN0ZSgiY2x1c3RlciIsIGNsdXN0ZXJzLnNlbFtpXSkpCiAgcGVha3MgPSBydW5NQUNTKAogICAgICBvYmo9c25hcC5vdXRbd2hpY2goc25hcC5vdXRAbWV0YURhdGEkYmFyY29kZSAlaW4lIGNvbG5hbWVzKHRjZWxscy5zY2UuYXRhYylbdGNlbGxzLnNjZS5hdGFjJHNldXJhdF9jbHVzdGVycz09Y2x1c3RlcnMuc2VsW2ldXSksXSwgCiAgICAgIG91dHB1dC5wcmVmaXg9cGFzdGUwKCJUY2VsbHNfRjc0X2NsdXN0ZXIiLCBjbHVzdGVycy5zZWxbaV0pLAogICAgICBwYXRoLnRvLnNuYXB0b29scz0iL29wdC9jb25kYS9iaW4vc25hcHRvb2xzIiwKICAgICAgcGF0aC50by5tYWNzPSIvb3B0L2NvbmRhL2Jpbi9tYWNzMiIsCiAgICAgIGdzaXplPSJocyIsICMgbW0sIGhzLCBldGMKICAgICAgYnVmZmVyLnNpemU9NTAwLCAKICAgICAgbnVtLmNvcmVzPTMsCiAgICAgIG1hY3Mub3B0aW9ucz0iLS1ub21vZGVsIC0tc2hpZnQgMTAwIC0tZXh0IDIwMCAtLXF2YWwgNWUtMiAtQiAtLVNQTVIiLAogICAgICB0bXAuZm9sZGVyPXRlbXBkaXIoKQogKQpwZWFrcwp9LCBtYy5jb3Jlcz01KQpwZWFrcy5uYW1lcyA9IHN5c3RlbSgibHMgfCBncmVwIG5hcnJvd1BlYWsiLCBpbnRlcm49VFJVRSkKcGVhay5nci5scyA9IGxhcHBseShwZWFrcy5uYW1lcywgZnVuY3Rpb24oeCl7CiAgcGVhay5kZiA9IHJlYWQudGFibGUoeCkKICBHUmFuZ2VzKHBlYWsuZGZbLDFdLCBJUmFuZ2VzKHBlYWsuZGZbLDJdLCBwZWFrLmRmWywzXSkpCn0pCnBlYWsuZ3IgPSByZWR1Y2UoUmVkdWNlKGMsIHBlYWsuZ3IubHMpKQoKIyMgTWFrZSBjZWxsIGJ5IHBlYWsgbWF0cml4CgpwZWFrcwpgYGAKClVzaW5nIGNocm9tVkFSIGltcGxlbWVudGF0aW9uIGluIHNuYXBBVEFDCmBgYHtyfQpsaWJyYXJ5KGNocm9tVkFSKQpsaWJyYXJ5KG1vdGlmbWF0Y2hyKQpsaWJyYXJ5KEJTZ2Vub21lLkhzYXBpZW5zLlVDU0MuaGczOCkKCgoKIyBzbmFwLm91dCA9IG1ha2VCaW5hcnkoeC5zcCwgInBtYXQiKQpzbmFwLm91dEBtbWF0ID0gcnVuQ2hyb21WQVIoCiAgb2JqPXNuYXAub3V0W3doaWNoKHNuYXAub3V0QG1ldGFEYXRhJGJhcmNvZGUgJWluJSBkcHQuYmluLmNlbGxzKSxdLAogIGlucHV0Lm1hdD0iYm1hdCIsCiAgZ2Vub21lPUJTZ2Vub21lLkhzYXBpZW5zLlVDU0MuaGczOCwKICAjIG1pbi5jb3VudD0xMCwKICBzcGVjaWVzPSJIb21vIHNhcGllbnMiCik7Cnguc3A7CmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9N30KCmFjYy5kZiA8LSBkYXRhLmZyYW1lKHQoYXMubWF0cml4KGFjYy5tYXRbaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLHN0cl9zdWJzZXQoY29sbmFtZXMoYWNjLm1hdCksIHBhdHRlcm4gPSAiQVRBQ18uKy0xIildKSkpICU+JQogIG11dGF0ZV9hbGwoYXMuZmFjdG9yKQoKdGVzdC5kZiA8LSBjYmluZChhY2MuZGZbLGludGVyc2VjdChodmcucm5hLCBjb2xuYW1lcyhhY2MuZGYpKSBdLCBkcHQ9ZHB0LmRmJGRwdF9wc2V1ZG90aW1lW2RwdC5kZiR0ZWNoPT0iQVRBQyJdKQpmaXQuZHB0IDwtIGxtKGRwdCB+IC4sIGRhdGE9dGVzdC5kZikKCmZpdC5jb2VmZnMgPC0gZml0LmRwdCRjb2VmZmljaWVudHNbMjpsZW5ndGgoZml0LmRwdCRjb2VmZmljaWVudHMpXQpoaXN0KGZpdC5jb2VmZnMsIGJyZWFrcyA9IDEwMCkKd2hpY2goYWJzKGZpdC5jb2VmZnMpID4gMC4xKQphbm92YS5kcHQgPC0gYW5vdmEoZml0LmRwdCkKYXNzb2NpYXRlZC5nZW5lcyA8LSByb3duYW1lcyhhbm92YS5kcHQpW3doaWNoKGFub3ZhLmRwdCRgUHIoPkYpYCA8IDAuMDEpXQoKYWNjLm1hdCAlPiUgZGltKCkKCmRwdC5iaW4uYWNjLm1hdCA8LSBhY2MubWF0W2Fzc29jaWF0ZWQuZ2VuZXMsIHN0cl9zdWJzZXQoY29sbmFtZXMoYWNjLm1hdCksIHBhdHRlcm4gPSAiQVRBQ18uKy0xIildICU+JQogIGFzLm1hdHJpeCgpICU+JQogIHJlc2hhcGUyOjptZWx0KHZhcm5hbWVzPWMoImdlbmUiLCAiY2VsbCIpKSAlPiUKICBsZWZ0X2pvaW4oZHB0LmRmKSAlPiUKICBncm91cF9ieShkcHRfYmluLCBnZW5lKSAlPiUKICBzdW1tYXJpc2UodmFsdWU9c3VtKHZhbHVlKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJkcHRfYmluIiwgdmFsdWVzX2Zyb20gPSAidmFsdWUiKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoJ2dlbmUnKSAlPiUKICBhcy5tYXRyaXgoKQoKcGhlYXRtYXA6OnBoZWF0bWFwKGRwdC5iaW4uYWNjLm1hdCAlPiUgdCgpICU+JSBzY2FsZSgpICU+JSB0KCksIGNsdXN0ZXJfY29scyA9IEYsIGNvbG9yID0gdmlyaWRpczo6dmlyaWRpcygxMCkpCiAgCgpnZ3Bsb3QoYWVzKGRwdF9iaW4sIGdlbmUsIGZpbGw9dmFsdWUpKSArCiAgZ2VvbV90aWxlKCkKYGBgCgoKCgoKCgoKCgoKCgo=